package com.changgou.search.service.Impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.changgou.goods.Feign.skuFeign;
import com.changgou.goods.pojo.Sku;
import com.changgou.goods.pojo.Spec;
import com.changgou.search.dao.SkuMapper;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SkuService;
import com.changgou.util.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.discovery.PropertyBasedAzToRegionMapper;
import com.sun.org.apache.bcel.internal.generic.NEW;
import org.apache.lucene.search.SortField;
import org.aspectj.weaver.ast.Var;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.SlowLogLevel;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.similarity.ScriptedSimilarity;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.UnmappedTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.NotWritablePropertyException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Example;

import javax.print.Doc;
import java.util.*;

/**
 * @ClassName: SkuServiceImpl
 * @Author: ZWJ
 * @Date: 2020-08-29 16:14
 * @Synopsis: Null
 **/
@Service
public class SkuServiceImpl implements SkuService {

    @Autowired
    private SkuMapper skuMapper;
    @Autowired
    private skuFeign skuFeign;
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;


    /***
     * 搜索sku
     * @param searchMap
     * @return
     */
    @Override
    public Map search(Map<String, String> searchMap) {

        Map resultMap = new HashMap();

        //创建查询构造器
        NativeSearchQueryBuilder builder = buildBasicQuery(searchMap);

        //条件查询：关键字+品牌+分类+聚合
        resultMap = searchList(builder, searchMap);
   /*    // resultMap.put("skuList", skuInfos);
        //聚合查询
        Map<String, Object> groupMap = groupAll(builder, searchMap);
        resultMap.put("groupMap", groupMap);*/

        return resultMap;
        /*//规格查询
        Map<String, Set<String>> setMap = specMap(builder);
        resultMap.put("skuSpec", setMap);*/

       /* if (StringUtils.isEmpty(searchMap.get("brand"))){
            //品牌查询
            List<String> brandList = brandList(builder);
            resultMap.put("brandList", brandList);
        }
       if (StringUtils.isEmpty(searchMap.get("category"))){
           //分类查询
           List<String> categoryLIst = categoryLIst(builder);
           resultMap.put("categoryList", categoryLIst);
       }*/

    }
    /**
     * 构建基本查询
     *
     * @param searchMap
     * @return
     */
    private NativeSearchQueryBuilder buildBasicQuery(Map<String, String> searchMap) {
        // 查询构建器
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (searchMap != null) {
            //1.关键字查询
            if (!StringUtils.isEmpty(searchMap.get("keywords"))) {
                //关键字查询；boost：权重
                boolQueryBuilder.must(QueryBuilders
                        .matchQuery("name", searchMap.get("keywords")).boost(10));
            }
            if (!StringUtils.isEmpty(searchMap.get("brand"))) {
                //品牌查询
                boolQueryBuilder.must(QueryBuilders.termQuery("brandName", searchMap.get("brand")));
            }
            if (!StringUtils.isEmpty(searchMap.get("category"))) {
                //分类查询
                boolQueryBuilder.must(QueryBuilders.termQuery("categoryName", searchMap.get("category")));
            }
            //规格查询
            for (Map.Entry<String, String> stringStringEntry : searchMap.entrySet()) {
                //判断是否有规格查询
                String key = stringStringEntry.getKey();
                if (key.startsWith("spec_")) {
                    //有规格查询
                    //获取规格查询的值
                    String value = stringStringEntry.getValue();
                    //截取规格名字
                    String substring = key.substring(5);
                    //规格查询设置
                    boolQueryBuilder.must(QueryBuilders.termQuery("specMap." + substring + ".keyword", value));
                }
            }
            //价格查询
            if (!StringUtils.isEmpty(searchMap.get("price"))) {
                //获取传入价格的值
                String price = searchMap.get("price");
                //将-以及元进行替换
                String replace = price.replace('元', '-');
                //切割字符串
                String[] split = replace.split("-");
                //设置价格条件的下限
                boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gt(split[0]));
                if (split.length > 1) {
                    //设置价格的上限(包含)
                    boolQueryBuilder.must(QueryBuilders.rangeQuery("price").lte(split[1]));
                }
            }
        }else {
            builder.withQuery(QueryBuilders.matchAllQuery());
        }

        //设置分页
        Integer pageNum = getPageNum(searchMap); //获取页码
        Integer pageSize = 12; //设置每页多少数据
        PageRequest pageRequest = PageRequest.of(pageNum, pageSize);
        //设置分页查询
        builder.withPageable(pageRequest);

        //排序实现
        //获取排序规则
        String sortRule = searchMap.get("sortRule");
        //排序字段
        String sortField = searchMap.get("sortField");
        if (!StringUtils.isEmpty(sortField)&&!StringUtils.isEmpty(sortRule)) {
            builder.withSort(SortBuilders.fieldSort(sortField).order(SortOrder.valueOf(sortRule)));
        }else {
            builder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC));
        }
        //-----------------------聚合查询开始----------------------------
        if (StringUtils.isEmpty(searchMap.get("brand"))) {
            //构建品牌查询
            builder.addAggregation(AggregationBuilders.terms("brand").field("brandName").size(100000));
        }
        if (StringUtils.isEmpty(searchMap.get("category"))) {
            //构建分类查询
            builder.addAggregation(AggregationBuilders.terms("category").field("categoryName").size(100000));
        }
        //构建规格查询   1.1
        builder.addAggregation(AggregationBuilders.terms("spec").field("spec.keyword").size(100000));
        //---------------聚合查询结束-------------------------

        builder.withQuery(boolQueryBuilder);
        return builder;
    }

    //优化聚合查询 (品牌+分类)1.0--->1.1（规格）
    public Map<String, Object> groupAll(NativeSearchQueryBuilder builder, Map<String, String> searchMap) {
        Map<String, Object> objectMap = new HashMap<>();
        if (StringUtils.isEmpty(searchMap.get("brand"))) {
            //构建品牌查询
            builder.addAggregation(AggregationBuilders.terms("brand").field("brandName").size(100000));
        }
        if (StringUtils.isEmpty(searchMap.get("category"))) {
            //构建分类查询
            builder.addAggregation(AggregationBuilders.terms("category").field("categoryName").size(100000));
        }
        //构建规格查询   1.1
        builder.addAggregation(AggregationBuilders.terms("spec").field("spec.keyword").size(100000));
        //执行查询,获取结果
        AggregatedPage<SkuInfo> skuInfos = elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);
        Aggregations aggregations = skuInfos.getAggregations();

        if (StringUtils.isEmpty(searchMap.get("brand"))) {
            //获取品牌结果并处理结果
            List<Object> brandList = groupAllResults("brand", aggregations);
            objectMap.put("brandList", brandList);
        }
        if (StringUtils.isEmpty(searchMap.get("category"))) {
            //获取分类结果并处理结果
            List<Object> categoryList = groupAllResults("category", aggregations);
            objectMap.put("categoryList", categoryList);
        }
        //获取规格结果并处理结果
        List<Object> spectList = groupAllResults("spec", aggregations);
        Map<String, Set<String>> setMap = groupAllSpec(spectList);
        objectMap.put("specList",setMap);
        //存储数据
        return objectMap;
    }

    //聚合查询的结果集处理
    public List<Object> groupAllResults(String data, Aggregations aggregations) {
        List<Object> objectList = new ArrayList<>();
        //获取data名字的数据结果集
        StringTerms stringTerms = aggregations.get(data);
        for (StringTerms.Bucket bucket : stringTerms.getBuckets()) {
            //取出每条数据
            String keyAsString = bucket.getKeyAsString();
            //存入集合
            objectList.add(keyAsString);
        }
        return objectList;
    }
//聚合查询优化处理规格数据
    public  Map<String,Set<String>>  groupAllSpec(List<Object> specs){
        Map<String,Set<String>> setMap=new HashMap<>();
        for (Object spec : specs) {
            //获取每条数据转成map
            Map<String,String> map = JSONObject.parseObject((String) spec, Map.class);
            //将数据遍历
            for (Map.Entry<String, String> stringStringEntry : map.entrySet()) {
                //获取key：规格名字
                String key = stringStringEntry.getKey();
                //获取值：规格的值
                String value = stringStringEntry.getValue();
                //查看存放数据中是否有规格名字的键
                Set<String> stringSet = setMap.get(key);
                if (stringSet==null){
                    //没有则创建存放规格值的map值
                    stringSet=new HashSet<>();
                }
                stringSet.add(value);
                setMap.put(key,stringSet);
            }

        }
        return setMap;
    }


    public Integer getPageNum(Map<String, String> searchMap) {
        //获取页码
        String pageNum = searchMap.get("pageNum");
        if (pageNum != null) {
            try {
                //对页码进行转换与判断
                int i = Integer.parseInt(pageNum);
                if (i <= 0) {
                    //页码小于等于0
                    return 0;
                }
                //输入页码-1，返回真正查看的页码
                return i - 1;
            } catch (NumberFormatException e) {
                //页码转换类型失败默认返回1
                return 0;
            }
        }
        //没有指定页码，默认为第0页
        return 0;
    }

    /**
     * 规格查询列表
     *
     * @param builder
     * @return
     */
    public Map<String, Set<String>> specMap(NativeSearchQueryBuilder builder) {
        //返回结果
        Map<String, Set<String>> setMap = new HashMap<>();
        //设置查询域
        builder.addAggregation(AggregationBuilders.terms("spec").field("spec.keyword").size(100000));
        //执行查询
        AggregatedPage<SkuInfo> skuInfos = elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);
        //得到结果包装集
        Aggregations aggregations = skuInfos.getAggregations();
        //获取结果
        StringTerms specs = aggregations.get("spec");
        //判断是否有数据
        if (specs == null || specs.getBuckets() == null) {
            //没有数据则返回null
            return null;
        }
        //遍历结果集
        for (StringTerms.Bucket bucket : specs.getBuckets()) {
            //获取每条数据的json格式字符串
            String keyAsString = bucket.getKeyAsString();
            //将字符转换为map
            Map<String, String> map = JSONObject.parseObject(keyAsString, Map.class);
            //将数据进行遍历
            for (Map.Entry<String, String> stringEntry : map.entrySet()) {
                //获取规格名字
                String key = stringEntry.getKey();
                //获取规格的值
                String value = stringEntry.getValue();
                //从map中取出该key的值，将新的value存入然后再放入map中
                Set<String> stringSet = setMap.get(key);
                if (stringSet == null) {
                    //map中没有这个key/规格存在
                    stringSet = new HashSet<>();
                }
                stringSet.add(value);
                setMap.put(key, stringSet);
            }

        }
        return setMap;
    }

    /***
     * 查询分类列表
     * @param builder
     * @return
     */
    public List<String> categoryLIst(NativeSearchQueryBuilder builder) {
        List<String> stringList = new ArrayList<>();
        //设置查询条件
        builder.addAggregation(AggregationBuilders.terms("category").field("categoryName").size(100000));
        //执行查询
        AggregatedPage<SkuInfo> skuInfos = elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);
        //获取数据
        Aggregations aggregations = skuInfos.getAggregations();
        StringTerms categorys = aggregations.get("category");
        for (StringTerms.Bucket bucket : categorys.getBuckets()) {
            String keyAsString = bucket.getKeyAsString();
            stringList.add(keyAsString);
        }
        return stringList;
    }

    /***
     * 查询品牌列表
     * @param builder
     * @return
     */
    public List<String> brandList(NativeSearchQueryBuilder builder) {
        List<String> brandList = new ArrayList<>();
        //设置查询条件
        builder.addAggregation(AggregationBuilders.terms("brand").field("brandName").size(100000));
        //执行条件查询，得到查询结果
        AggregatedPage<SkuInfo> skuInfos = elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);
        //获取查询结果
        Aggregations aggregations = skuInfos.getAggregations();
        //通过别名获取查询到的品牌结果
        StringTerms brands = aggregations.get("brand");
        for (StringTerms.Bucket bucket : brands.getBuckets()) {
            //取出获取到的品牌内容
            String keyAsString = bucket.getKeyAsString();
            brandList.add(keyAsString);
        }
        return brandList;
    }

    /***
     * 关键字搜索
     * @param builder
     * @return
     */
    private Map searchList(NativeSearchQueryBuilder builder,Map<String, String> searchMap) {
        Map resultMap = new HashMap();
        //高亮域配置
        HighlightBuilder.Field field = new HighlightBuilder.Field("name"); //指定高亮域
        field.preTags("<font style='color:red'>") //设置前缀
                .postTags("</font>")  //设置后缀
                .fragmentOffset(100); //数组里面100
        builder.withHighlightFields(field);
        //查询解析器
        NativeSearchQuery searchQuery = builder.build();
        //执行关键字查询
        Page<SkuInfo> skuPage = elasticsearchTemplate
                .queryForPage(searchQuery, SkuInfo.class, new SearchResultMapper() {
                    @Override
                    public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                        List<T> tList = new ArrayList<>();
                        //获取结果集
                        SearchHits hits = searchResponse.getHits();
                        for (SearchHit hit : hits) {
                            //获取每条数据的json格式
                            String sourceAsString = hit.getSourceAsString();
                            //将json格式转换为javabean
                            SkuInfo skuInfo = JSONObject.parseObject(sourceAsString, SkuInfo.class);
                            //获取高亮数据
                            HighlightField name = hit.getHighlightFields().get("name");
                            if (name != null) {
                                String data = "";
                                Text[] fragments = name.getFragments();
                                for (Text fragment : fragments) {
                                    //输出看看fragment:华为Mate 版手机 全网通 亮黑色(+)  全网通  <font style='color:red'>红色</font>  高通865  IOS  128G  5200万
                                    // System.out.println(fragment.toString());
                                    data = data + fragment.toString();
                                }
                                //将数据替换成高亮数据格式
                                skuInfo.setName(data);
                            }
                            tList.add((T) skuInfo);
                        }
                        return new AggregatedPageImpl<T>(tList, pageable, searchResponse.getHits().getTotalHits(),searchResponse.getAggregations());
                    }
                });
        List<SkuInfo> content = skuPage.getContent();
        resultMap.put("skuList",content);
        //-------------------------聚合查询结果开始----------------------
        //执行查询,获取结果
        AggregatedPage<SkuInfo> skuInfos = elasticsearchTemplate.queryForPage(builder.build(), SkuInfo.class);
        Aggregations aggregations = skuInfos.getAggregations();

        if (StringUtils.isEmpty(searchMap.get("brand"))) {
            //获取品牌结果并处理结果
            List<Object> brandList = groupAllResults("brand", aggregations);
            resultMap.put("brandList", brandList);
        }
        if (StringUtils.isEmpty(searchMap.get("category"))) {
            //获取分类结果并处理结果
            List<Object> categoryList = groupAllResults("category", aggregations);
            resultMap.put("categoryList", categoryList);
        }
        //获取规格结果并处理结果
        List<Object> spectList = groupAllResults("spec", aggregations);
        Map<String, Set<String>> setMap = groupAllSpec(spectList);
        resultMap.put("specList",setMap);
        //-------------------------聚合查询结果结束-----------------------
        resultMap.put("pageNum",builder.build().getPageable().getPageNumber()+1);//获取页面当前页
        resultMap.put("pageSize",builder.build().getPageable().getPageSize());//获取一页多少数据
        resultMap.put("totalElements",skuPage.getTotalElements());//总记录数
        return resultMap;
    }


    /***
     * 导入SKU数据
     */
    @Override
    public void importSku() {
        Result<List<Sku>> byStatus = skuFeign.findByStatus("1");
        List<Sku> data = byStatus.getData();
        //将数据转换为json格式字符串
        //ObjectMapper objectMapper=new ObjectMapper();
        //String dataString = objectMapper.writeValueAsString(data);
        String dataString = JSONObject.toJSONString(data);
        //将json格式字符串转换为SkuInfo字符串
        List<SkuInfo> skuInfos = JSONObject.parseArray(dataString, SkuInfo.class);
        //将保存规格信息的对象转换为map格式数据
        for (SkuInfo skuInfo : skuInfos) {
            String spec = skuInfo.getSpec();
            //将json格式字符串转成map格式
            Map<String, Object> specMap = JSONObject.parseObject(spec, Map.class);
            //讲述据存入
            skuInfo.setSpecMap(specMap);
        }
        //讲述据存入es
        skuMapper.saveAll(skuInfos);
    }

}
